/* 网络通信模块 */

#pragma once
#include "UserManager.hpp"
#include "MsgQueue.hpp"
#include "ChatMsg.hpp"
#include <unistd.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <string.h>

#define THREADCOUNT 4 

class ChatServer
{
public:
    ChatServer()
        : _tcp_sockfd(-1)
        , _tcp_port(PORT)
        , _epoll_fd(-1)
        , _user_mana(NULL)
        , _thread_count(THREADCOUNT)
        , _ready_sockfd_que(NULL)
        , _recv_que(NULL)
        , _send_que(NULL)
    {}

    ~ChatServer(){}

    // 1.初始化资源接口
    int InitChatServer(uint16_t tcp_port = PORT, int thread_count = THREADCOUNT)
    {
        _tcp_port = tcp_port;
        _thread_count = thread_count;

        /* tcp通信 */
        _tcp_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_tcp_sockfd < 0)
        {
            perror("socket");
            return -1;
        }
        
        // 端口重用--防止服务端在time_wait状态重启
        int opt = 1;
        setsockopt(_tcp_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        struct sockaddr_in ser_addr;
        ser_addr.sin_family = AF_INET;
        ser_addr.sin_port = htons(_tcp_port);
        ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
        int ret = bind(_tcp_sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));    //绑定
        if(ret < 0)
        {
            perror("bind");
            return -1;
        }
        
        ret = listen(_tcp_sockfd, 1024);    //监听
        if(ret < 0)
        {
            perror("listen");
            return -1;
        }

        // epoll初始化
        _epoll_fd = epoll_create(3);
        if(_epoll_fd < 0)
        {
            perror("epoll_create");
            return -1;
        }

        /* 用户管理模块 */
        _user_mana = new UserManager();
        if(_user_mana == NULL)
        {
            return -1;
        }
        if(_user_mana->InitUserManager() == false)
        {
            return -1;
        }

        /* 队列模块 */
        _ready_sockfd_que = new MsgQueue<int>();
        if(_ready_sockfd_que == NULL)
        {
            return -1;
        }

        _recv_que = new MsgQueue<ChatMsg>();
        if(_recv_que == NULL)
        {
            return -1;
        }

        _send_que = new MsgQueue<ChatMsg>();
        if(_send_que == NULL)
        {
            return -1;
        }
        
        return 0;
    }

    // 2.启动子线程接口（main函数调用启动）
    int StartChatServer()
    {
        // 1)创建epoll_wait线程
        pthread_t tid; 
        int ret = pthread_create(&tid, NULL, epoll_wait_start, (void*)this);
        if(ret < 0)
        {
            perror("pthread_create epoll_wait()");
            return -1;
        }

#if 0
        // 2)创建接收线程
        ret = pthread_create(&tid, NULL, recv_msg_start, (void*)this);
        if(ret < 0)
        {
            perror("pthread_create recv_msg_start()");
            return -1;
        }
#endif

        // 3)创建发送线程
        ret = pthread_create(&tid, NULL, send_msg_start, (void*)this);
        if(ret < 0)
        {
            perror("pthread_create send_msg_start()");
            return -1;
        }

        // 4)创建工作线程
        for(int i = 0; i < _thread_count; i++)
        {
            ret = pthread_create(&tid, NULL, deal_start, (void*)this);
            if(ret < 0)
            {
                perror("pthread_create deal_start()");
                _thread_count--;
            } 
        }

        if(_thread_count <= 0)
        {
            return -1;
        }

        // 5)主线程循环accept新连接
        struct sockaddr_in cli_addr;
        socklen_t cli_addr_len = sizeof(cli_addr);
        while(1)
        {
            int new_sockfd = accept(_tcp_sockfd, (struct sockaddr*)&cli_addr, &cli_addr_len);    //接收新连接套接字
            if(new_sockfd < 0)
            {
                continue;
            }

            struct epoll_event ee;
            ee.events = EPOLLIN;
            ee.data.fd = new_sockfd;
            epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, new_sockfd, &ee);    //添加新连接套接字至epoll监控
        }
        return 0;
    }

    // 3.epoll_wait线程启动函数
    static void* epoll_wait_start(void* arg)
    {
        pthread_detach(pthread_self());    //线程分离
        ChatServer* cs = (ChatServer*)arg;

        while(1)
        {
            // 1)循环epoll_wait 
            struct epoll_event ee_arr[10];
            int ret = epoll_wait(cs->_epoll_fd, ee_arr, sizeof(ee_arr)/sizeof(ee_arr[0]), -1);   
            if(ret < 0)
            {
                continue;
            }

            // 2)添加epoll捕获的事件（epoll只监听了new_socket）
            for(int i = 0; i < ret; ++i)    
            {
#if 0
                cs->_ready_sockfd_que->push(ee_arr[i].data.fd);
#endif

                char buf[TCP_DATA_MAX_LEN] = {0};
                
                //【注】TCP粘包问题
                
                ssize_t recv_size = recv(ee_arr[i].data.fd, buf, sizeof(buf)-1, 0);    //接收new_socket
                if(recv_size < 0)    //接受失败
                {
                    std::cout << "recv faild, sockfd is " << ee_arr[i].data.fd << std::endl;
                    continue;
                }
                else if(recv_size == 0)    //对端关闭
                {
                    epoll_ctl(cs->_epoll_fd, EPOLL_CTL_DEL, ee_arr[i].data.fd, NULL);    //剔除epoll监控
                    close(ee_arr[i].data.fd);    //关闭该套接字
                    continue;
                }

                // 正常recv
                printf("\nepoll_wait resv socket[%d]:%s\n", ee_arr[i].data.fd, buf);    //for test
                
                // 添加Msg至消息队列
                std::string msg; 
                msg.assign(buf, strlen(buf));    //赋值至msg，并清空buf
                ChatMsg chat_msg;
                chat_msg.GetChatMsg(ee_arr[i].data.fd, msg);

                cs->_recv_que->push(chat_msg);
            }
        }
        return NULL;
    }

#if 0
    // 4.接收线程启动函数————！！有问题！！
    static void* recv_msg_start(void* arg)
    {
        pthread_detach(pthread_self());    //线程分离
        ChatServer* cs = (ChatServer*)arg;

        while(1)
        {
            // 从就绪队列获取new_sockfd
            int sockfd = -1;
            cs->_ready_sockfd_que->pop(&sockfd);

            //char buf[TCP_DATA_MAX] = {0};
            Msg msg;
            msg._sockfd = sockfd;

            ssize_t recv_size = recv(sockfd, msg._buf, sizeof(msg._buf)-1, 0);    //接收new_socket
            if(recv_size < 0)    //接受失败
            {
                std::cout << "recv faild, sockfd is " << sockfd << std::endl;
                continue;
            }
            else if(recv_size == 0)    //对端关闭
            {
                epoll_ctl(cs->_epoll_fd, EPOLL_CTL_DEL,sockfd, NULL);    //剔除epoll监控
                close(sockfd);    //关闭该套接字
                continue;
            }

            // 正常recv
            printf("epoll_wait resv socket[%d]:%s\n", sockfd, msg._buf);    //for test
            // 添加Msg至消息队列
            cs->_send_que->push(msg);
        }
        
        return NULL;
    }
#endif
    
    // 5.发送线程启动函数
    static void* send_msg_start(void* arg)
    {
        pthread_detach(pthread_self());    //线程分离
        ChatServer* cs = (ChatServer*)arg;
        
        while(1)
        {
            ChatMsg chat_msg;
            cs->_send_que->pop(&chat_msg);    //获取队列数据
            
            std::string msg;
            chat_msg.GetStringMsg(&msg);
            
            printf("send to %d：%s\n", chat_msg._sockfd, msg.c_str());    //for test
            //std::cout << "send thread: " << msg << std::endl;    //for test

            int ret = send(chat_msg._sockfd, msg.c_str(), msg.size(), 0);    //发送数据
            if(ret < 0)
            {
                perror("send");
                continue;
            }

        }
        return NULL;
    }
    
    // 6.工作线程启动函数
    static void* deal_start(void* arg)
    {
        pthread_detach(pthread_self());    //线程分离
        ChatServer* cs = (ChatServer*)arg;
        
        while(1)
        {
            // 1)从接受队列获取消息
            ChatMsg chat_msg;
            cs->_recv_que->pop(&chat_msg);
            
            // 2)通过消息类型分业务处理
            int msg_type = chat_msg._msg_type;
            switch(msg_type)
            {
                case Register:
                {
                    cs->DealRegister(chat_msg); 
                    break;
                }
                case Login:
                {
                    cs->DealLogin(chat_msg);
                    break;
                }
                case AddFriend:
                {
                    cs->DealAddFriend(chat_msg);
                    break;
                }
                case PushAddFriendMsg_Resp:
                {
                    cs->DealAddFriendResp(chat_msg);
                    break;
                }
                case SendMsg: 
                {
                    cs->DealSendMsg(chat_msg);
                    break;
                }
                case GetFriendMsg:
                {
                    cs->GetAllFriendInfo(chat_msg);
                    break;
                }
                default:
                {
                    break;
                }
            }
            
        }
        
        return NULL;
    }

    /* 工作线程处理模块 */
    // 1)注册
    void DealRegister(ChatMsg& chat_msg)
    {
        // 获取注册信息
        std::string nickname = chat_msg.GetValue("nickname");
        std::string school = chat_msg.GetValue("school");
        std::string telnum = chat_msg.GetValue("telnum");
        std::string passwd = chat_msg.GetValue("passwd");
        
        // 用户管理系统注册接口
        int userid;
        int ret = _user_mana->DearRegister(&userid, nickname, school, telnum, passwd);
        
        // 回复应答
        chat_msg.Clear();
        chat_msg._msg_type = Register_Resp;
        if(ret < 0)
        {
            chat_msg._reply_status = REGISTER_FAILED;
        }else
        {
            chat_msg._reply_status = REGISTER_SUCCESS;
        }
        chat_msg._user_id = userid;
        
        _send_que->push(chat_msg);
    }

    // 2)登陆
    void DealLogin(ChatMsg& chat_msg)
    {
        std::string telnum = chat_msg.GetValue("telnum");
        std::string passwd = chat_msg.GetValue("passwd");
 
        int ret = _user_mana->DearLogin(telnum, passwd, chat_msg._sockfd);

        chat_msg.Clear();
        chat_msg._msg_type = Login_Resp;
        if(ret < 0)
        {
            chat_msg._reply_status = LOGIN_FAILED;
        }else
        {
            chat_msg._reply_status = LOGIN_SUCCESS;
        }
        chat_msg._user_id = ret;
        
        _send_que->push(chat_msg);
    }

    // 3)添加好友
    void DealAddFriend(ChatMsg& chat_msg)
    {
        // (1)获取信息
        std::string recvUser_tel = chat_msg.GetValue("telnum");    //接收方用户电话

        int sendUser_id = chat_msg._user_id;    //发送方用户id
        UserInfo sendUserInfo;    //发送方用户信息
        _user_mana->GetUserInfo(sendUser_id, &sendUserInfo);

        chat_msg.Clear();

        // (2)查询接收方用户存在信息及登陆状态
        UserInfo recvUserInfo;    //接收方用户信息
        int ret = _user_mana->IsLogin(recvUser_tel, &recvUserInfo);
        if(ret == -1)    // a.接收方不存在
        {
            // 直接向发送方用户发送反馈
            chat_msg._msg_type = AddFriend_Resp;
            chat_msg._reply_status = ADDFRIEND_FAILED;
            chat_msg.SetValue("content", "recv user not exits, please check friend telnum.");

            _send_que->push(chat_msg);
            return;
        }
        else if(ret == OFFLINE)    // b.接收方未在线
        {
            std::cout << recvUserInfo._nickname + " status is OFFLINE" << std::endl;
            // 存入消息缓存队列，择机发送
            /*未实现*/
            return;
        }
        // c.接收方ONLINE
        // (3)为接收方用户推送添加好友请求
        chat_msg._sockfd = recvUserInfo._tcp_socket;
        chat_msg._msg_type = PushAddFriendMsg;
        chat_msg.SetValue("sendUser_id", sendUserInfo._userid);
        chat_msg.SetValue("sendUser_nickname", sendUserInfo._nickname);
        chat_msg.SetValue("sendUser_school", sendUserInfo._school);
        chat_msg.SetValue("sendUser_telnum", sendUserInfo._telnum);

        _send_que->push(chat_msg);
    }

    // 4)添加好友应答
    void DealAddFriendResp(ChatMsg& chat_msg)
    {
        // (1)获取信息
        int reply_status = chat_msg._reply_status;
        
        int recvUser_id = chat_msg._user_id;    // 接收方用户信息
        UserInfo recvUserInfo;
        _user_mana->GetUserInfo(recvUser_id, &recvUserInfo);
        
        int sendUser_id = atoi(chat_msg.GetValue("userid").c_str());// 发送方用户信息
        UserInfo sendUserInfo;
        _user_mana->GetUserInfo(sendUser_id, &sendUserInfo);

        chat_msg.Clear();

        // (2)判断响应状态
        chat_msg._sockfd = sendUserInfo._tcp_socket;
        chat_msg._msg_type = AddFriend_Resp;
        if(reply_status == ADDFRIEND_FAILED)
        {
            chat_msg._reply_status = ADDFRIEND_FAILED;
            std::string content = "add user " + recvUserInfo._nickname + " failed";
            chat_msg.SetValue("content", content);
        }
        else if(reply_status == ADDFRIEND_SUCCESS)
        {
            chat_msg._reply_status = ADDFRIEND_SUCCESS;
            std::string content = "add user " + recvUserInfo._nickname + " success";
            chat_msg.SetValue("content", content);
            chat_msg.SetValue("recvUser_id", recvUserInfo._userid);
            chat_msg.SetValue("recvUser_nickname", recvUserInfo._nickname);
            chat_msg.SetValue("recvUser_school", recvUserInfo._school);
            chat_msg.SetValue("recvUser_telnum", recvUserInfo._telnum);

            _user_mana->setFriend(sendUser_id, recvUser_id);    //更新数据库
        }

        // (3)为发送方用户发送反馈
        if(sendUserInfo._user_status == OFFLINE)
        {
            // 存入缓存队列，择机发送
            /* 未实现 */
        }
        _send_que->push(chat_msg);
    }

    // 5)获取用户好友信息
    void GetAllFriendInfo(ChatMsg& chat_msg)
    {
        int user_id = chat_msg._user_id;
        chat_msg.Clear();
        chat_msg._msg_type = GetFriendMsg_Resp;
       
        std::vector<int> fri_arr;
        if(_user_mana->GetFriends(user_id, &fri_arr))
        {
            chat_msg._reply_status = GETFRIEND_SUCCESS;
        }
        else{
            chat_msg._reply_status = GETFRIEND_FAILED;
        }
        
        for(size_t i = 0; i < fri_arr.size(); ++i)
        {
            UserInfo cur;
            _user_mana->GetUserInfo(fri_arr[i], &cur);

            Json::Value val;
            val["userid"] = cur._userid;
            val["nickname"] = cur._nickname;
            val["school"] = cur._school;
            val["telnum"] = cur._telnum;

            chat_msg._json_msg.append(val);
        }
        // 发送Json数组
        _send_que->push(chat_msg);
    }

    // 6)发送消息
    void DealSendMsg(ChatMsg& chat_msg)
    {
        // 获取消息
        int send_id = chat_msg._user_id;
        int recv_id = chat_msg._json_msg["recvmsgid"].asInt();
        std::string send_msg = chat_msg._json_msg["msg"].asString();
        chat_msg.Clear();

        // 为发送方提供反馈
        UserInfo recv_ui;
        bool ret = _user_mana->GetUserInfo(recv_id, &recv_ui);
        if(ret == false || recv_ui._user_status == OFFLINE)
        {
            /* 用户不存在：消息发送失败
             * 用户不在线：发送方消息缓冲
             * */
            chat_msg._msg_type = SendMsg_Resp;
            chat_msg._reply_status = SENDMSG_FAILED;
            
            _send_que->push(chat_msg);  //添加队列
            return;
        }
        else
        {
            chat_msg._msg_type = SendMsg_Resp;
            chat_msg._reply_status = SENDMSG_SUCCESS;

            _send_que->push(chat_msg);  //添加队列
        }

        // 为接收方转发
        UserInfo send_ui;
        ret = _user_mana->GetUserInfo(send_id, &send_ui);

        chat_msg.Clear();
        chat_msg._msg_type = PushMsg;
        chat_msg._sockfd = recv_ui._tcp_socket;
        chat_msg.SetValue("sendUser_id", send_id);
        chat_msg.SetValue("sendUser_nickname", send_ui._nickname);
        chat_msg.SetValue("sendUser_school", send_ui._school);
        chat_msg.SetValue("sendUser_telnum", send_ui._telnum);
        chat_msg.SetValue("sendUser_msg", send_msg);

        _send_que->push(chat_msg);  //添加队列
    }

private:
    /* tcp通信模块 */
    int _tcp_sockfd;    //侦听套接字
    int _tcp_port;      //端口
    
    int _epoll_fd;      //epoll操作句柄

    /* 用户管理模块 */
    UserManager* _user_mana;

    /* 工作线程模块 */
    int _thread_count;  //工作线程数量

    /* 消息队列模块 */
    MsgQueue<int>* _ready_sockfd_que;    //就绪文件描述符队列
    MsgQueue<ChatMsg>* _recv_que;    //接收线程队列（存放recv数据）
    MsgQueue<ChatMsg>* _send_que;    //发送线程队列（存放send数据）
};
